﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;

namespace KanbanizeAPI
{
    public class KanbanizeXmlResultParser : KanbanizeResultParser
    {
        public KanbanizeXmlResultParser() { }


        /// <summary>
        /// Creates a KanbanizeSuccessResult from a successful create_new_task API response
        /// </summary>
        /// <param name="xml">The XML returned to the library by the API.</param>
        /// <returns>A populated KanbanizeSuccessResult object.</returns>
        public KanbanizeSuccessResult SuccessResultFromCreateTask(string xml)
        {
            KanbanizeSuccessResult result = null;

            XmlDocument doc = LoadDocumentFromString(xml);
            XmlNode node = doc.SelectSingleNode("/xml/taskid");

            if (node != null)
            {
                result = new KanbanizeSuccessResult();
                result.Success = int.Parse(node.InnerText) > 0;
            }

            return result;
        }

        /// <summary>
        /// Error check for xml results returned that are not standard responses to API requests
        /// </summary>
        /// <param name="xml">The string of XML returned to the library by the API.</param>
        /// <returns>A KanbanizeError object containing some information with regard to the error.</returns>
        public KanbanizeError ErrorCheck(string xml)
        {
            KanbanizeError error = null;
            
            XmlDocument doc = LoadDocumentFromString(xml);
            XmlNode node = doc.SelectSingleNode("/xml/error");

            if (node != null)
            {
                error = new KanbanizeError();
                error.ErrorString = doc.SelectSingleNode("/xml/errorMessage").InnerText;
            }

            return error;
        }

        /// <summary>
        /// Provides a populated KanbanLogin object from a successful API login
        /// </summary>
        /// <param name="xml">The xml string returned to the library by the API.</param>
        /// <returns>A KanbanizeLogin object.</returns>
        public KanbanizeLogin KanbanizeLoginFromLoginRequest(string xml)
        {
            KanbanizeLogin login = new KanbanizeLogin();
            XmlNode node = LoadDocumentFromString(xml).SelectSingleNode("/xml");

            // Properly formatted response has 6 child nodes...
            if (node.ChildNodes.Count == 6)
            {
                login.EmailAddress = node.SelectSingleNode("email").InnerText;
                login.Username = node.SelectSingleNode("username").InnerText;
                login.RealName = node.SelectSingleNode("realname").InnerText;
                login.CompanyName = node.SelectSingleNode("companyname").InnerText;

                string timeZoneString = node.SelectSingleNode("timezone").InnerText;
                int hours = int.Parse(timeZoneString.Split(new char[] { ':' })[0]);
                int minutes = int.Parse(timeZoneString.Split(new char[] { ':' })[1]);

                login.TimeZone = new TimeSpan(hours, minutes, 0);

                login.ApiKey = node.SelectSingleNode("apikey").InnerText;

                login.Success = true;
            }
            else
            {
                login.Success = false;
            }
            
            return login;
        }


        /// <summary>
        /// Provides a list of KanbanizeProject objects complete with their respective
        /// KanbanizeBoard lists.
        /// </summary>
        /// <param name="xml">The xml string returned to the library by the API.</param>
        /// <returns>A list populated with KanbanizeProject objects if there are any, null otherwise.</returns>
        public List<KanbanizeProject> ProjectsFromProjectsAndBoardsResult(string xml)
        {
            List<KanbanizeProject> projectList = null;
            XmlNodeList nodes = LoadDocumentFromString(xml).SelectNodes("/xml/projects/item");

            if (nodes.Count > 0)
            {
                projectList = new List<KanbanizeProject>();

                foreach (XmlNode node in nodes)
                {
                    KanbanizeProject project = new KanbanizeProject();
                    project.Name = node.SelectSingleNode("name").InnerText;
                    project.Id = int.Parse(node.SelectSingleNode("id").InnerText);

                    XmlNodeList boardNodes = node.SelectNodes("boards/item");

                    if (boardNodes.Count > 0)
                    {
                        foreach (XmlNode boardNode in boardNodes)
                        {
                            KanbanizeBoard board = new KanbanizeBoard();
                            board.Name = boardNode.SelectSingleNode("name").InnerText;
                            board.Id = int.Parse(boardNode.SelectSingleNode("id").InnerText);

                            project.Boards.Add(board);
                        }
                    }

                    projectList.Add(project);
                }
            }

            return projectList;
        }

        /// <summary>
        /// Provides a list of KanbanizeUser objects from a get_board_settings result.
        /// </summary>
        /// <param name="xml">The xml string returned to the library by the API</param>
        /// <returns>A list populated with KanbanizeUser objects if there are any, null otherwise.</returns>
        public List<KanbanizeUser> UsersFromSettingsResult(string xml)
        {
            List<KanbanizeUser> userList = null;
            XmlNodeList nodes = LoadDocumentFromString(xml).SelectNodes("/xml/usernames/item");

            if (nodes.Count > 0)
            {
                userList = new List<KanbanizeUser>();

                foreach (XmlNode node in nodes)
                {
                    KanbanizeUser user = new KanbanizeUser();
                    user.Username = node.InnerText;
                    userList.Add(user);
                }
            }

            return userList;
        }

        /// <summary>
        /// Provides a list of KanbanizeTemplate objects from a get_board_settings result.
        /// </summary>
        /// <param name="xml">The xml string returned to the library by the API.</param>
        /// <returns>A list populated with KanbanizeTemplate objects if there are any, null otherwise.</returns>
        public List<KanbanizeTemplate> TemplatesFromSettingsResult(string xml)
        {
            List<KanbanizeTemplate> templateList = null;
            XmlNodeList nodes = LoadDocumentFromString(xml).SelectNodes("/xml/templates/item");

            if (nodes.Count > 0)
            {
                templateList = new List<KanbanizeTemplate>();

                foreach (XmlNode node in nodes)
                {
                    KanbanizeTemplate template = new KanbanizeTemplate();
                    template.TemplateName = node.InnerText;
                    templateList.Add(template);
                }
            }

            return templateList;
        }

        /// <summary>
        /// Provides a list of KanbanizeType objects from a get_board_settings result.
        /// </summary>
        /// <param name="xml">The xml string returned to the library by the API.</param>
        /// <returns>A list populated with KanbanizeType objects if there are any, null otherwise.</returns>
        public List<KanbanizeType> TypesFromSettingsResult(string xml)
        {
            List<KanbanizeType> typeList = null;
            XmlNodeList nodes = LoadDocumentFromString(xml).SelectNodes("/xml/types/item");

            if (nodes.Count > 0)
            {
                typeList = new List<KanbanizeType>();

                foreach (XmlNode node in nodes)
                {
                    KanbanizeType type = new KanbanizeType();
                    type.TypeName = node.InnerText;
                    typeList.Add(type);
                }
            }

            return typeList;
        }

        /// <summary>
        /// Provides a breakdown of column and lane structure objects from a get_board_structure result.
        /// </summary>
        /// <param name="xml">The xml string returned to the library by the API.</param>
        /// <returns>A KanbanizeBoard object with populated BoardColumns and BoardLanes members (should they exist).</returns>
        public KanbanizeBoard BoardStructureFromStructureResult(string xml)
        {
            KanbanizeBoard board = new KanbanizeBoard();

            XmlNodeList nodes = LoadDocumentFromString(xml).SelectNodes("/xml/columns/item");

            if (nodes.Count > 0)
            {

                // the api does not return backlog and archive columns, so let's do that here for completeness.

                KanbanizeBoardColumn column = new KanbanizeBoardColumn();
                column.Position = int.MinValue; // let's give this a big negative value as the first column from our board will be indexed at 0
                column.LcName = "Backlog";
                column.Description = "The backlog.";
                column.TasksPerRow = int.MaxValue;

                board.BoardColumns.Add(column);

                foreach (XmlNode node in nodes)
                {
                    column = new KanbanizeBoardColumn();

                    column.Position = int.Parse(node.SelectSingleNode("position").InnerText);
                    column.LcName = node.SelectSingleNode("lcname").InnerText;
                    column.Description = node.SelectSingleNode("description").InnerText;
                    column.TasksPerRow = int.Parse(node.SelectSingleNode("tasksperrow").InnerText);

                    board.BoardColumns.Add(column);
                }

                column = new KanbanizeBoardColumn();
                column.Position = int.MaxValue; // let's give this a big positive value as the last column from our board will be indexed at <number of columns> which will be well less than int.MaxValue
                column.LcName = "Archive";
                column.Description = "The archive.";
                column.TasksPerRow = int.MaxValue;

                board.BoardColumns.Add(column);
            }

            nodes = LoadDocumentFromString(xml).SelectNodes("/xml/lanes/item");

            if(nodes.Count > 0)
            {
                foreach (XmlNode node in nodes)
                {
                    KanbanizeBoardLane lane = new KanbanizeBoardLane();

                    lane.LcName = node.SelectSingleNode("lcname").InnerText;
                    lane.ColorCode = node.SelectSingleNode("color").InnerText;
                    lane.Description = node.SelectSingleNode("description").InnerText;

                    board.BoardLanes.Add(lane);
                }
            }

            return board;
        }

        /// <summary>
        /// Provides a means to get an XmlDocument from an XML string.
        /// </summary>
        /// <param name="xmlString">The XML document text.</param>
        /// <returns>An XmlDocument object if xmlString is valid XML, null otherwise.</returns>
        /// <throws>XmlException</throws>
        XmlDocument LoadDocumentFromString(string xmlString)
        {
            XmlDocument doc = new XmlDocument();

            try
            {
                doc.LoadXml(xmlString);
            }
            catch (Exception e)
            {
                doc.LoadXml("<xml><error>An error occurred during document load</error><errorMessage>" + e.Message + "</errorMessage></xml>");
            }

            return doc;
        }
    }
}
